<div class="config-setup-tool-element">
  <div class="cor-loader" ng-if="!config"></div>
  <div ng-show="true">
    <form id="configform" name="configform">
      <!-- Custom SSL certificates -->
      <div class="co-panel" id="custom-ssl">
        <div class="co-panel-heading">
          <i class="fa fa-certificate"></i> Custom SSL Certificates
        </div>
        <div class="co-panel-body">
          <div class="config-certificates-field"></div>
        </div>
      </div>

      <!-- Basic Configuration -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fas fa-cogs"></i> Basic Configuration
        </div>
        <div class="co-panel-body">
          <table class="config-table">
            <tr>
              <td>Enterprise Logo URL:</td>
              <td>
                <span class="config-string-field" binding="config.ENTERPRISE_LOGO_URL"
                      placeholder="http://example.com/logo.png"></span>
                <div class="help-text">
                  Enter the full URL to your company's logo.
                </div>
              </td>
              <td>
                <img class="registry-logo-preview" ng-src="{{ config.ENTERPRISE_LOGO_URL }}">
              </td>
            </tr>
            <tr>
              <td class="non-input">Contact Information:</td>
              <td colspan="2">
                <span class="config-contacts-field" binding="config.CONTACT_INFO"></span>
                <div class="help-text" style="margin-top: 10px;">
                  Information to show in the Contact Page. If none specified, CoreOS contact information
                  is displayed.
                </div>
              </td>
            </tr>
          </table>
        </div>
      </div>

      <!-- Server Configuration -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa fa-cloud"></i> Server Configuration
        </div>
        <div class="co-panel-body">
          <table class="config-table">
            <tr>
                <div class="co-alert co-alert-danger" ng-if="isFieldReadonly('hostname')">
                    The Hostname configuration is managed externally. These values
                    cannot be changed.
                </div>
            </tr>
            <tr>
              <td>Server Hostname:</td>
              <td>
                <div class="co-alert co-alert-danger" ng-if="config.SERVER_HOSTNAME.indexOf('quay.io') >=0"
                     style="margin-bottom: 10px;">
                  The domain name <code>quay.io</code> is reserved for legacy reasons and cannot be used in
                  <span class="registry-name"></span> installations.
                </div>
                <span class="config-string-field"
                      binding="config.SERVER_HOSTNAME"
                      placeholder="Hostname (and optional port if non-standard)"
                      pattern="{{ HOSTNAME_REGEX }}"
                      is-readonly="::isFieldReadonly('hostname')"></span>
                <div class="help-text">
                  The HTTP host (and optionally the port number if a non-standard HTTP/HTTPS port) of the location
                  where the registry will be accessible on the network
                </div>
              </td>
            </tr>
            <tr>
              <td>TLS:</td>
              <td>
                <select ng-disabled="::isFieldReadonly('hostname')" class="form-control" ng-model="mapped.TLS_SETTING">
                  <option value="internal-tls">Red Hat Quay handles TLS</option>
                  <option value="external-tls">My own load balancer handles TLS (Not Recommended)</option>
                  <option value="none">None (Not For Production)</option>
                </select>

                <div class="co-alert co-alert-danger" ng-if="mapped.TLS_SETTING == 'none'"
                     style="margin-bottom: 20px; margin-top: 10px;">
                  Running without TLS should not be used for production workloads!
                </div>

                <div class="co-alert co-alert-warning" ng-if="mapped.TLS_SETTING == 'external-tls'"
                     style="margin-bottom: 20px; margin-top: 10px;">
                  Terminating TLS outside of Red Hat Quay can result in unusual behavior if the external load balancer
                  is not
                  configured properly. <strong>This option is not recommended for simple setups</strong>. Please contact
                  support
                  if you encounter problems while using this option.
                </div>

                <div class="co-alert co-alert-info" ng-if="mapped.TLS_SETTING == 'internal-tls'"
                     style="margin-bottom: 20px; margin-top: 10px;">
                  Enabling TLS also enables <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security">HTTP
                  Strict Transport Security</a>.<br />
                  This prevents downgrade attacks and cookie theft, but browsers will reject all future insecure
                  connections on this hostname.
                </div>

                <table class="config-table" ng-if="mapped.TLS_SETTING == 'internal-tls' && !isFieldReadonly('hostname')">
                  <tr>
                    <td class="non-input">Certificate:</td>
                    <td>
                      <span class="config-file-field" filename="ssl.cert" has-file="hasfile.SSLCert"></span>
                      <div class="help-text">
                        The certificate must be in PEM format.
                      </div>
                    </td>
                  </tr>
                  <tr>
                    <td class="non-input">Private key:</td>
                    <td>
                      <span class="config-file-field" filename="ssl.key" has-file="hasfile.SSLKey"></span>
                    </td>
                  </tr>
                </table>
              </td>
            </tr>
          </table>

        </div>
      </div>

      <!-- Consistency Settings -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa ci-shared-database"></i> Data Consistency Settings
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>Relax constraints on consistency guarantees for specific operations
              to enable higher performance and availability.
            </p>
          </div>
          <table class="config-table">
            <tr>
              <td>
                <div class="config-bool-field" binding="config.ALLOW_PULLS_WITHOUT_STRICT_LOGGING">
                  Allow repository pulls even if audit logging fails.
                  <div class="help-text">
                    If enabled, failures to write to the audit log will fallback from
                    the database to the standard logger for registry pulls.
                  </div>
                </div>
              </td>
            </tr>
          </table>
        </div>
      </div> <!-- /Consistency Settings -->

      <!-- Time Machine -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa fa-history"></i> Time Machine
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>Time machine keeps older copies of tags within a repository for the configured period
              of time, after which they are garbage collected. This allows users to
              revert tags to older images in case they accidentally pushed a broken image. It is
              highly recommended to have time machine enabled, but it does take a bit more space
              in storage.
            </p>
          </div>
          <table class="config-table">
            <!-- This `ng-if` true is *required* to create a new scope so that the <form> inside
                 the config-list-field doesn't try to submit the *outer* form when the Add button
                 is clicked. This is highly stupid, but until we figure out a better way, such it
                 shall be.
            -->
            <tr ng-if="true">
              <td>Allowed expiration periods:</td>
              <td>
                <span class="config-list-field" item-title="Tag Expiration Period"
                      binding="config.TAG_EXPIRATION_OPTIONS" item-pattern="[0-9]+(m|w|h|d|s)"></span>
                <div class="help-text">
                  The expiration periods allowed for configuration. The default tag expiration *must* be in this list.
                </div>
              </td>
            </tr>
            <tr>
              <td>Default expiration period:</td>
              <td>
                <span class="config-string-field" binding="config.DEFAULT_TAG_EXPIRATION"
                      pattern="[0-9]+(m|w|h|d|s)"></span>
                <div class="help-text">
                  The default tag expiration period for all namespaces (users and organizations). Must be expressed in a
                  duration string form: <code>30m</code>, <code>1h</code>, <code>1d</code>, <code>2w</code>.
                </div>
              </td>
            </tr>
            <tr>
              <td>Allow users to select expiration:</td>
              <td>
                <div class="config-bool-field" binding="config.FEATURE_CHANGE_TAG_EXPIRATION">
                  Enable Expiration Configuration
                  <div class="help-text">
                    If enabled, users will be able to select the tag expiration duration for the namespace(s) they
                    administrate, from the configured list of options.
                  </div>
                </div>
              </td>
            </tr>
          </table>
        </div>
      </div> <!-- /Time Machine -->

      <!-- Redis -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <img src="/static/img/redis-small.png"> redis
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>A <a href="http://redis.io" ng-safenewtab>redis</a> key-value store is required for real-time events and
              build logs.</p>
          </div>

          <table class="config-table">
            <tr>
                <div class="co-alert co-alert-danger" ng-if="isFieldReadonly('redis')">
                    The Redis configuration is managed externally. These values
                    cannot be changed.
                </div>
            </tr>
            <tr>
              <td>Redis Hostname:</td>
              <td>
                <span class="config-string-field"
                      binding="mapped.redis.host"
                      placeholder="The redis server hostname"
                      pattern="{{ HOSTNAME_REGEX }}"
                      validator="validateHostname(value)"
                      is-readonly="::isFieldReadonly('redis')"></span>
              </td>
            </tr>
            <tr>
              <td>Redis port:</td>
              <td>
                <span class="config-numeric-field"
                      binding="mapped.redis.port"
                      default-value="6379"
                      is-readonly="::isFieldReadonly('redis')"></span>
                <div class="help-text">
                  Access to this port and hostname must be allowed from all hosts running
                  the enterprise registry
                </div>
              </td>
            </tr>
            <tr>
              <td>Redis password:</td>
              <td>
                <input class="form-control"
                       type="password"
                       ng-model="mapped.redis.password"
                       placeholder="Optional password for connecting to redis"
                       ng-readonly="::isFieldReadonly('redis')">
              </td>
            </tr>
          </table>
        </div>
      </div> <!-- /Redis -->

      <!-- Repository Mirroring -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa fa-sync"></i> Repository Mirroring
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>If enabled, scheduled mirroring of repositories from registries will be available.
          </div>

          <div class="config-bool-field" binding="config.FEATURE_REPO_MIRROR">
            Enable Repository Mirroring
          </div>
          <div class="co-alert co-alert-info" ng-if="config.FEATURE_REPO_MIRROR" style="margin-top: 20px;">
            A repository mirror service must be running to use this feature. Documentation
            on setting up and running this service can be found at <a href="https://access.redhat.com/documentation/en-us/red_hat_quay/3/html-single/manage_red_hat_quay/index#repository-mirroring" ng-safenewtab>Running Repository Mirroring Service</a>.
          </div>

          <div class="config-bool-field" binding="config.REPO_MIRROR_TLS_VERIFY" ng-if="config.FEATURE_REPO_MIRROR">
            Require HTTPS and verify certificates of Quay registry during mirror.
          </div>
        </div>
      </div>

      <!-- Registry Storage -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa fa-download"></i> Registry Storage
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>
              Registry images can be stored either locally or in a remote storage system.
              <div class="co-alert co-alert-danger">
                Do not use "Locally mounted directory" Storage Engine for any production configurations. Mounted NFS volumes are not supported. Local storage is meant for test-only installations.
              </div>
            </p>

            <div class="config-bool-field" binding="config.FEATURE_PROXY_STORAGE">
              Proxy storage via <span class="registry-name"></span>
              <div class="help-text">
                If enabled, all requests to storage engine(s) will be proxied through
                <span class="registry-name"></span>. Should only be
                enabled if storage cannot be directly accessed by external nodes talking to the registry.
              </div>
            </div>

            <div class="config-bool-field feature-storage-replication" binding="config.FEATURE_STORAGE_REPLICATION">
              Enable Storage Replication
              <div class="help-text">
                If enabled, replicates storage to other regions. See
                <a href="https://access.redhat.com/documentation/en-us/red_hat_quay/3/html-single/manage_red_hat_quay/index#georeplication-of-storage-in-quay"
                   ng-safenewtab>documentation</a> for more information.
              </div>
            </div>

            <div class="storage-config" ng-class="$last ? 'last' : ''" ng-repeat="sc in storageConfig">
              <table class="config-table">
                <tr>
                  <td class="non-input">Location ID:</td>
                  <td>
                    <input class="form-control" ng-if="allowChangeLocationStorageConfig(sc.location)"
                           ng-class="storageConfigError[$index].location ? 'ng-invalid' : ''" ng-model="sc.location"
                           ng-pattern="/^[a-zA-Z0-9_-]+$/" required>
                    <div ng-if="!allowChangeLocationStorageConfig(sc.location)">
                      {{ sc.location }}
                    </div>
                    <div class="co-alert co-alert-danger" ng-show="storageConfigError[$index].location">
                      {{ storageConfigError[$index].location }}
                    </div>
                    <div class="input-util" ng-if="allowRemoveStorageConfig(sc.location)"><a class="remove-link"
                                                                                             ng-click="removeStorageConfig(sc)">Remove</a></div>
                  </td>
                </tr>

                <tr ng-if="config.FEATURE_STORAGE_REPLICATION">
                  <td class="non-input">Set Default:</td>
                  <td>
                    <div class="config-bool-field" binding="sc.defaultLocation">
                      Replicate to storage engine by default
                    </div>
                  </td>
                </tr>

                <tr>
                  <td class="non-input">Storage Engine:</td>
                  <td>
                    <select class="form-control" ng-class="storageConfigError[$index].engine ? 'ng-invalid' : ''"
                            ng-model="sc.data[0]">
                      <option value="RHOCSStorage">Red Hat OpenShift Container Storage (NooBaa S3)</option>
                      <option value="LocalStorage">Locally mounted directory</option>
                      <option value="S3Storage">Amazon S3</option>
                      <option value="AzureStorage">Azure Blob Storage</option>
                      <option value="GoogleCloudStorage">Google Cloud Storage</option>
                      <option value="RadosGWStorage">Ceph Object Gateway (RADOS)</option>
                      <option value="SwiftStorage">OpenStack Storage (Swift)</option>
                      <option value="CloudFrontedS3Storage">CloudFront + Amazon S3</option>
                    </select>

                    <div class="co-alert co-alert-danger" ng-if="storageConfigError[$index].engine">
                      {{ storageConfigError[$index].engine }}
                    </div>
                  </td>
                </tr>

                <!-- Fields -->
                <tr ng-repeat="field in STORAGE_CONFIG_FIELDS[sc.data[0]]">
                  <td>{{ field.title }}:</td>
                  <td>
                    <span class="config-map-field" binding="sc.data[1][field.name]" ng-if="field.kind == 'map'"
                          keys="field.keys"></span>
                    <span class="config-string-field" binding="sc.data[1][field.name]"
                          placeholder="{{ field.placeholder }}" pattern="{{ field.pattern }}" ng-if="field.kind == 'text'"
                          is-optional="field.optional"></span>
                    <span class="config-password-field" binding="sc.data[1][field.name]"
                          placeholder="{{ field.placeholder }}" pattern="{{ field.pattern }}" ng-if="field.kind == 'password'"
                          is-optional="field.optional"></span>
                    <span class="config-bool-field" binding="sc.data[1][field.name]" ng-if="field.kind == 'bool'">
                      {{ field.placeholder }}
                    </span>
                    <span class="config-file-field" filename="{{ sc.location + '-' + field.filesuffix }}"
                          binding="sc.data[1][field.name]"
                          has-file="hasfile['contact-' + sc.location + '-' + field.filesuffix]"
                          ng-if="field.kind == 'file'"></span>
                    <div ng-if="field.kind == 'option'">
                      <select class="form-control" ng-model="sc.data[1][field.name]">
                        <option ng-repeat="value in field.values" value="{{ value }}"
                                ng-selected="sc.data[1][field.name] == value">{{ value }}</option>
                      </select>
                    </div>
                    <div class="help-text" ng-if="field.help_text">
                      {{ field.help_text }}
                    </div>
                    <div class="help-text" ng-if="field.help_url">
                      See <a href="{{ field.help_url }}" ng-safenewtab>Documentation</a> for more information
                    </div>
                  </td>
                </tr>
              </table>
            </div>

            <div class="add-storage-link" ng-if="canAddStorageConfig()">
              <a ng-click="addStorageConfig()">Add Additional Storage Engine</a>
            </div>
          </div>
        </div>
      </div>

      <!-- Action log Storage Configuration -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa fa-archive"></i> Action Log Storage Configuration
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>
              Action logs can be stored in the database or Elasticsearch.
              In the latter case, the actions logs can (optionally) be sent to a data stream first.
            </p>
          </div>
          <table class="config-table">
            <tr>
              <td>Logs storage:</td>
              <td>
                <select class="form-control" ng-model="mapped.LOGS_MODEL">
                  <option value="elasticsearch">Elasticsearch</option>
                  <option value="database">Database</option>
                </select>
              </td>
          </table>

          <table class="config-table" ng-if="mapped.LOGS_MODEL == 'elasticsearch'">
            <tr>
              <td><strong>Elasticsearch config</strong></td>
            </tr>
            <tr>
              <td>Elasticsearch hostname:</td>
              <td>
                <span class="config-string-field" binding="mapped.LOGS_MODEL_CONFIG.elasticsearch_config.host" placeholder="The Elasticsearch server hostname"
                      pattern="{{ HOSTNAME_REGEX }}" validator="validateHostname(value)"></span>
              </td>
            </tr>
            <tr>
              <td>Elasticsearch port:</td>
              <td>
                <span class="config-numeric-field" binding="mapped.LOGS_MODEL_CONFIG.elasticsearch_config.port" default-value="9200"></span>
                <div class="help-text">
                  Access to this port and hostname must be allowed from all hosts running
                  the enterprise registry
                </div>
              </td>
            </tr>
            <tr>
              <td>Elasticsearch access key:</td>
              <td>
                <span class="config-string-field" binding="mapped.LOGS_MODEL_CONFIG.elasticsearch_config.access_key"
                      placeholder="The Elasticsearch access key"
                </span>
              </td>
            </tr>
            <tr>
              <td>Elasticsearch secret key:</td>
              <td>
                <span class="config-password-field" binding="mapped.LOGS_MODEL_CONFIG.elasticsearch_config.secret_key"
                      placeholder="The Elasticsearch secret key"
                </span>
              </td>
            </tr>
            <tr>
              <td>AWS region:</td>
              <td>
                <span class="config-string-field" binding="mapped.LOGS_MODEL_CONFIG.elasticsearch_config.aws_region"
                      placeholder="The AWS region (optional)" is-optional="true"
                </span>
              </td>
            </tr>
            <tr>
              <td>Index prefix:</td>
              <td>
                <span class="config-string-field" binding="mapped.LOGS_MODEL_CONFIG.elasticsearch_config.index_prefix"
                      default-value="logentry_" placeholder="The Elasticsearch index_prefix"
                </span>
              </td>
            </tr>
            <tr>
              <td class="non-input">Logs Producer:</td>
              <td>
                <select class="form-control" ng-model="mapped.LOGS_MODEL_CONFIG.producer">
                  <option value="kinesis_stream">Kinesis</option>
                  <option value="elasticsearch">Elasticsearch</option>
                </select>
              </td>
            </tr>
          </table>
          <table class="config-table" ng-if="mapped.LOGS_MODEL_CONFIG.producer == 'kinesis_stream'">
            <tr>
              <td><strong>Logs producer options</strong></td>
            </tr>
            <tr>
              <td>Stream name:</td>
              <td>
                <span class="config-string-field" binding="mapped.LOGS_MODEL_CONFIG.kinesis_stream_config.stream_name"
                      placeholder="The Kinesis stream name"
                </span>
              </td>
            </tr>
            <tr>
              <td>AWS access key:</td>
              <td>
                <span class="config-string-field" binding="mapped.LOGS_MODEL_CONFIG.kinesis_stream_config.aws_access_key"
                      placeholder="The AWS access key"
                </span>
              </td>
            </tr>
            <tr>
              <td>AWS secret key:</td>
              <td>
                <span class="config-password-field" binding="mapped.LOGS_MODEL_CONFIG.kinesis_stream_config.aws_secret_key"
                      placeholder="The AWS secret key"
                </span>
              </td>
            </tr>
            <tr>
              <td>AWS region:</td>
              <td>
                <span class="config-string-field" binding="mapped.LOGS_MODEL_CONFIG.kinesis_stream_config.aws_region"
                      placeholder="The AWS region"
                </span>
              </td>
            </tr>
          </table>
        </div>
      </div>

      <!-- Action log archiving -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa fa-archive"></i> Action Log Rotation and Archiving
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>
              All actions performed in <span class="registry-name"></span> are automatically logged. These logs are
              stored in a database table, which can become quite large.
              Enabling log rotation and archiving will move all logs older than 30 days into storage.
            </p>
          </div>
          <div class="config-bool-field" binding="config.FEATURE_ACTION_LOG_ROTATION">
            Enable Action Log Rotation
          </div>

          <table class="config-table" ng-if="config.FEATURE_ACTION_LOG_ROTATION">
            <tr>
              <td>Storage location:</td>
              <td>
                <select class="form-control" ng-model="config.ACTION_LOG_ARCHIVE_LOCATION">
                  <option ng-repeat="sc in storageConfig" value="{{ sc['location'] }}">{{ sc['location'] }}</option>
                </select>
                <div class="help-text">
                  The storage location in which to place archived action logs. Logs will only be archived to this single
                  location.
                </div>
              </td>
            </tr>
            <tr>
              <td>Storage path:</td>
              <td>
                <span class="config-string-field" binding="config.ACTION_LOG_ARCHIVE_PATH"
                      placeholder="Path under storage to place archived logs"></span>
                <div class="help-text">
                  The path under the configured storage engine in which to place the archived logs in JSON form.
                </div>
              </td>
            </tr>
            <tr>
              <td>Log Rotation Threshold:</td>
              <td>
                <span class="config-string-field" binding="config.ACTION_LOG_ROTATION_THRESHOLD"
                      pattern="[0-9]+(m|w|h|d|s)"></span>
                <div class="help-text">
                  The number of days after which to archive action logs to storage. Must be expressed in a duration
                  string form: <code>30m</code>, <code>1h</code>, <code>1d</code>, <code>2w</code>.
                </div>
                <div class="help-text">
                  Note: The rotation threshold should be considered a balancing act between user history and size of
                  database.
                </div>
                <div class="help-text">
                  Larger time windows will result in more logs, but give users more history to view.
                  Anything less than <code>2w</code> is not recommended.
                </div>
              </td>
            </tr>
          </table>
        </div>
      </div>

      <!-- Security Scanner -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa fa-bug"></i> Security Scanner
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>If enabled, all images pushed to <span class="registry-name"></span> will be scanned via the external
              security scanning service, with vulnerability information available in the UI and API, as well
              as async notification support.
            </p>
          </div>

          <div class="config-bool-field" binding="config.FEATURE_SECURITY_SCANNER">
            Enable Security Scanning
          </div>
          <div class="co-alert co-alert-info" ng-if="config.FEATURE_SECURITY_SCANNER" style="margin-top: 20px;">
            A scanner compliant with the Quay Security Scanning API must be running to use this feature. Documentation
            on running <a href="https://github.com/coreos/clair" ng-safenewtab>Clair</a> can be found at <a
                                                                                                           href="https://access.redhat.com/documentation/en-us/red_hat_quay/3/html-single/manage_red_hat_quay/index#quay-security-scanner" ng-safenewtab>Running Clair Security
            Scanner</a>.
          </div>

          <table class="config-table" ng-if="config.FEATURE_SECURITY_SCANNER">
            <tr>
              <td>Authentication Key:</td>
              <td>
                <span class="config-service-key-field"
                      service-name="{{ config.SECURITY_SCANNER_ISSUER_NAME || 'secscan' }}"></span>
                <div class="help-text">
                  The security scanning service requires an authorized service key to speak to Quay. Once setup, the
                  key
                  can be managed in the Service Keys panel under the Super User Admin Panel.
                </div>
              </td>
            </tr>
            <tr>
              <td>Security Scanner Endpoint:</td>
              <td>
                <span class="config-string-field" binding="config.SECURITY_SCANNER_ENDPOINT"
                      placeholder="Security Scanner API endpoint (Example: http://myhost:6060)"
                      pattern="http(s)?://.+"></span>
                <div class="help-text">
                  The HTTP URL at which the security scanner is running.
                </div>
                <div class="co-alert co-alert-info" ng-if="config.SECURITY_SCANNER_ENDPOINT.indexOf('https:') == 0"
                     style="margin-top: 20px;">
                  Is the security scanner behind a domain signed with a <strong>self-signed TLS certificate</strong>?
                  If so, please make sure to register your SSL CA in the <a href="#custom-ssl">custom certificates
                  panel</a> above.
                </div>
              </td>
            </tr>
          </table>
        </div>
      </div>

      <!-- App Registry -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa ci-appcube"></i> Application Registry
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>If enabled, an additional registry API will be available for managing applications (Kubernetes
              manifests, Helm charts) via the <a href="https://github.com/app-registry">App Registry
              specification</a>. A great place to get started is to install the
              <a href="https://github.com/app-registry/appr-helm-plugin">Helm Registry Plugin</a>.
          </div>

          <div class="config-bool-field" binding="config.FEATURE_APP_REGISTRY">
            Enable App Registry
          </div>
        </div>
      </div>

      <!-- ACI Conversion (DEPRECATED) -->
      <div class="co-panel" ng-show="originalConfig.FEATURE_ACI_CONVERSION">
        <div class="co-panel-heading">
          <i class="fa rocket-icon"
             style="width: 20px; height: 20px; background-size: cover; vertical-align: middle;"></i> <a
                                                                                                       href="http://github.com/coreos/rkt" ng-safenewtab>rkt</a> Conversion (DEPRECATED)
        </div>
        <div class="co-panel-body">
          <div class="co-alert co-alert-warning" style="margin-bottom: 20px">
            ACI conversion support has been <strong>officially deprecated</strong> by Quay and support will be
            removed in the next version. It is <strong>strongly</strong> suggested to disable this feature now.
          </div>

          <div class="description">
            <p>If enabled, all images in the registry can be fetched via <code>rkt fetch</code> or any other <a
                                                                                                               href="https://github.com/appc/spec/blob/master/spec/discovery.md" ng-safenewtab>AppC
              discovery</a>-compliant implementation.</p>
          </div>

          <div class="config-bool-field" binding="config.FEATURE_ACI_CONVERSION">
            Enable ACI Conversion
          </div>

          <table class="config-table" ng-if="config.FEATURE_ACI_CONVERSION">
            <tr>
              <td class="non-input">GPG2 Public Key File:</td>
              <td>
                <span class="config-file-field" filename="signing-public.gpg"
                      has-file="hasfile.gpgSigningPublic"></span>
                <div class="help-text">
                  The certificate must be in PEM format.
                </div>
              </td>
            </tr>
            <tr>
              <td class="non-input">GPG2 Private Key File:</td>
              <td>
                <span class="config-file-field" filename="signing-private.gpg"
                      has-file="hasfile.gpgSigningPrivate"></span>
              </td>
            </tr>
            <tr>
              <td class="non-input">GPG2 Private Key Name:</td>
              <td>
                <span class="config-string-field" binding="config.GPG2_PRIVATE_KEY_NAME"
                      placeholder="Name of the private key in the private key file (Example: EAB32227)"></span>
              </td>
            </tr>
          </table>
        </div>
      </div>

      <!-- E-mail -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa fa-envelope"></i> E-mail
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>Valid e-mail server configuration is required for notification e-mails and the ability of
              users to reset their passwords.</p>
          </div>

          <div class="config-bool-field" binding="config.FEATURE_MAILING">
            Enable E-mails
          </div>

          <table class="config-table" ng-if="config.FEATURE_MAILING">
            <tr>
              <td>SMTP Server:</td>
              <td>
                <span class="config-string-field" binding="config.MAIL_SERVER"
                      placeholder="SMTP server for sending e-mail" pattern="{{ HOSTNAME_REGEX }}"
                      validator="validateHostname(value)">></span>
              </td>
            </tr>
            <tr>
              <td>SMTP Server Port:</td>
              <td>
                <span class="config-numeric-field" binding="config.MAIL_PORT" default-value="587"></span>
              </td>
            </tr>
            <tr>
              <td>TLS:</td>
              <td>
                <div class="config-bool-field" binding="config.MAIL_USE_TLS">
                  Require TLS
                </div>
              </td>
            </tr>
            <tr>
              <td>Mail Sender:</td>
              <td>
                <input class="form-control" type="email" ng-model="config.MAIL_DEFAULT_SENDER"
                       placeholder="E-mail address"></span>
                <div class="help-text">
                  E-mail address from which all e-mails are sent. If not specified,
                  <code>support@quay.io</code> will be used.
                </div>
              </td>
            </tr>
            <tr>
              <td>Authentication:</td>
              <td>
                <div class="config-bool-field" binding="config.MAIL_USE_AUTH">
                  Requires Authentication
                </div>

                <table class="config-table" ng-if="config.MAIL_USE_AUTH">
                  <tr>
                    <td>Username:</td>
                    <td>
                      <span class="config-string-field" binding="config.MAIL_USERNAME"
                            placeholder="Username for authentication"></span>
                    </td>
                  </tr>
                  <tr>
                    <td>Password:</td>
                    <td>
                      <input class="form-control" type="password" ng-model="config.MAIL_PASSWORD"
                             placeholder="Password for authentication"></span>
                    </td>
                  </tr>
                </table>
              </td>
            </tr>

          </table>
        </div>
      </div> <!-- /E-mail -->

      <!-- Internal Authentication -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa fa-users"></i> Internal Authentication
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>
              Authentication for the registry can be handled by either the registry itself, LDAP, Keystone, or
              external JWT endpoint.
            </p>
            <p>
              Additional <strong>external</strong> authentication providers (such as GitHub) can be used in addition
              for <strong>login into the UI</strong>.
            </p>
          </div>

          <div ng-if="config.AUTHENTICATION_TYPE != 'AppToken'">
            <div class="co-alert co-alert-warning"
                 ng-if="config.AUTHENTICATION_TYPE != 'Database' && !config.FEATURE_REQUIRE_ENCRYPTED_BASIC_AUTH">
              It is <strong>highly recommended</strong> to require encrypted client passwords. External passwords used
              in the Docker client will be stored in <strong>plaintext</strong>!
              <a ng-click="config.FEATURE_REQUIRE_ENCRYPTED_BASIC_AUTH = true">Enable this requirement now</a>.
            </div>

            <div class="co-alert co-alert-success"
                 ng-if="config.AUTHENTICATION_TYPE != 'Database' && config.FEATURE_REQUIRE_ENCRYPTED_BASIC_AUTH">
              Note: The "Require Encrypted Client Passwords" feature is currently enabled which will
              prevent passwords from being saved as plaintext by the Docker client.
            </div>
          </div>

          <table class="config-table" style="margin-bottom: 20px;">
            <tr>
              <td class="non-input">Authentication:</td>
              <td>
                <select class="form-control" ng-model="config.AUTHENTICATION_TYPE">
                  <option value="Database">Local Database</option>
                  <option value="LDAP">LDAP</option>
                  <option value="Keystone">Keystone (OpenStack Identity)</option>
                  <option value="JWT">JWT Custom Authentication</option>
                  <option value="AppToken" ng-if="config.FEATURE_APP_SPECIFIC_TOKENS">External Application Token
                  </option>
                </select>
              </td>
            </tr>

            <tr ng-if="config.AUTHENTICATION_TYPE == 'LDAP' || config.AUTHENTICATION_TYPE == 'Keystone'">
              <td>Team synchronization:</td>
              <td>
                <div class="config-bool-field" binding="config.FEATURE_TEAM_SYNCING">
                  Enable Team Synchronization Support
                </div>
                <div class="help-text">
                  If enabled, organization administrators who are also superusers can set teams to have their
                  membership synchronized with a backing group in {{ config.AUTHENTICATION_TYPE }}.
                </div>
              </td>
            </tr>
            <tr
              ng-if="(config.AUTHENTICATION_TYPE == 'LDAP' || config.AUTHENTICATION_TYPE == 'Keystone') && config.FEATURE_TEAM_SYNCING">
              <td>Resynchronization duration:</td>
              <td>
                <span class="config-string-field" binding="config.TEAM_RESYNC_STALE_TIME"
                      pattern="[0-9]+(m|h|d|s)"></span>
                <div class="help-text">
                  The duration before a team must be re-synchronized. Must be expressed in a duration string form:
                  <code>30m</code>, <code>1h</code>, <code>1d</code>.
                </div>
              </td>
            </tr>
            <tr
              ng-if="(config.AUTHENTICATION_TYPE == 'LDAP' || config.AUTHENTICATION_TYPE == 'Keystone') && config.FEATURE_TEAM_SYNCING">
              <td>Self-service team syncing setup:</td>
              <td>
                <div class="co-alert co-alert-warning" style="margin-bottom: 20px;">If enabled, this feature will
                  allow *any organization administrator* to read the membership of any
                  {{ config.AUTHENTICATION_TYPE }} group.</div>
                <div class="config-bool-field" binding="config.FEATURE_NONSUPERUSER_TEAM_SYNCING_SETUP">
                  Allow non-superusers to enable and manage team syncing
                </div>
                <div class="help-text">
                  If enabled, non-superusers will be able to enable and manage team sycning on teams under
                  organizations in which they are administrators.
                </div>
              </td>
            </tr>
          </table>

          <!--  Keystone Authentication -->
          <table class="config-table" ng-if="config.AUTHENTICATION_TYPE == 'Keystone'">
            <tr>
              <td>Keystone API Version:</td>
              <td>
                <select ng-model="config.KEYSTONE_AUTH_VERSION">
                  <option value="2">2.0</option>
                  <option value="3">V3</option>
                </select>
              </td>
            </tr>
            <tr>
              <td>Keystone Authentication URL:</td>
              <td>
                <span class="config-string-field" binding="config.KEYSTONE_AUTH_URL" pattern="http(s)?://.+"></span>
                <div class="help-text">
                  The URL (starting with http or https) of the Keystone Server endpoint for auth.
                </div>
              </td>
            </tr>
            <tr>
              <td>Keystone Administrator Username:</td>
              <td>
                <span class="config-string-field" binding="config.KEYSTONE_ADMIN_USERNAME"></span>
                <div class="help-text">
                  The username for the Keystone admin.
                </div>
              </td>
            </tr>
            <tr>
              <td>Keystone Administrator Password:</td>
              <td>
                <input type="password" ng-model="config.KEYSTONE_ADMIN_PASSWORD" class="form-control" required></span>
                <div class="help-text">
                  The password for the Keystone admin.
                </div>
              </td>
            </tr>
            <tr>
              <td>Keystone Administrator Tenant:</td>
              <td>
                <span class="config-string-field" binding="config.KEYSTONE_ADMIN_TENANT"></span>
                <div class="help-text">
                  The tenant (project/group) that contains the administrator user.
                </div>
              </td>
            </tr>
          </table>

          <!-- JWT Custom Authentication -->
          <div class="co-alert co-alert-info" ng-if="config.AUTHENTICATION_TYPE == 'JWT'">
            JSON Web Token authentication allows your organization to provide an HTTP endpoint that
            verifies user credentials on behalf of <span class="registry-name"></span>.
            <br>
            Documentation
            on the API required can be found here: <a href="https://github.com/coreos/jwt-auth-example"
                                                      ng-safenewtab>https://github.com/coreos/jwt-auth-example</a>.
          </div>

          <table class="config-table" ng-if="config.AUTHENTICATION_TYPE == 'JWT'">
            <tr>
              <td>Authentication Issuer:</td>
              <td>
                <span class="config-string-field" binding="config.JWT_AUTH_ISSUER"></span>
                <div class="help-text">
                  The id of the issuer signing the JWT token. Must be unique to your organization.
                </div>
              </td>
            </tr>
            <tr>
              <td>Public Key:</td>
              <td>
                <span class="config-file-field" filename="jwt-authn.cert" has-file="hasfile.JWTCert"></span>
                <div class="help-text">
                  A certificate containing the public key portion of the key pair used to sign
                  the JSON Web Tokens. This file must be in PEM format.
                </div>
              </td>
            </tr>
            <tr>
              <td>User Verification Endpoint:</td>
              <td>
                <span class="config-string-field" binding="config.JWT_VERIFY_ENDPOINT" pattern="http(s)?://.+"></span>
                <div class="help-text">
                  The URL (starting with http or https) on the JWT authentication server for verifying username and
                  password credentials.
                </div>

                <div class="help-text" style="margin-top: 6px;">
                  Credentials will be sent in the <code>Authorization</code> header as Basic Auth, and this endpoint
                  should return <code>200 OK</code> on success (or a <code>4**</code> otherwise).
                </div>
              </td>
            </tr>
            <tr>
              <td>User Query Endpoint:</td>
              <td>
                <span class="config-string-field" binding="config.JWT_QUERY_ENDPOINT" pattern="http(s)?://.+"
                      is-optional="true"></span>
                <div class="help-text">
                  The URL (starting with http or https) on the JWT authentication server for looking up
                  users based on a prefix query. This is optional.
                </div>

                <div class="help-text" style="margin-top: 6px;">
                  The prefix query will be sent as a query parameter with name <code>query</code>.
                </div>
              </td>
            </tr>
            <tr>
              <td>User Lookup Endpoint:</td>
              <td>
                <span class="config-string-field" binding="config.JWT_GETUSER_ENDPOINT" pattern="http(s)?://.+"
                      is-optional="true"></span>
                <div class="help-text">
                  The URL (starting with http or https) on the JWT authentication server for looking up
                  a user by username or email address.
                </div>

                <div class="help-text" style="margin-top: 6px;">
                  The username or email address will be sent as a query parameter with name <code>username</code>.
                </div>
              </td>
            </tr>
          </table>

          <!-- LDAP Authentication -->
          <table class="config-table" ng-if="config.AUTHENTICATION_TYPE == 'LDAP'">
            <tr>
              <td>LDAP URI:</td>
              <td>
                <span class="config-string-field" binding="config.LDAP_URI" pattern="ldap(s)?://.+"></span>
                <div class="help-text">
                  The full LDAP URI, including the ldap:// or ldaps:// prefix.
                </div>
              </td>
            </tr>
            <tr>
              <td>Base DN:</td>
              <td>
                <span class="config-string-list-field" item-title="DN piece" item-delimiter=","
                      binding="config.LDAP_BASE_DN"></span>
                <div class="help-text">
                  A Distinguished Name path which forms the base path for looking up all LDAP records.
                </div>
                <div class="help-text">
                  Example: dc=my,dc=domain,dc=com
                </div>
              </td>
            </tr>
            <tr>
              <td>User Relative DN:</td>
              <td>
                <span class="config-string-list-field" item-title="RDN piece" item-delimiter=","
                      binding="config.LDAP_USER_RDN"></span>
                <div class="help-text">
                  A Distinguished Name path which forms the base path for looking up all user LDAP records,
                  relative to the Base DN defined above.
                </div>
                <div class="help-text">
                  Example: ou=employees
                </div>
              </td>
            </tr>
            <tr>
              <td>Secondary User Relative DNs:</td>
              <td>
                <span class="config-list-field" item-title="RDN" binding="config.LDAP_SECONDARY_USER_RDNS"></span>
                <div class="help-text">
                  A list of Distinguished Name path(s) which forms the secondary base path(s) for
                  looking up all user LDAP records, relative to the Base DN defined above. These path(s)
                  will be tried if the user is not found via the primary relative DN.
                </div>
                <div class="help-text">
                  Example: [ou=employees]
                </div>
              </td>
            </tr>
            <tr>
              <td>Additional User Filter Expression:</td>
              <td>
                <div class="co-alert co-alert-warning" style="margin-bottom: 10px;">
                  <strong>NOTE:</strong> This query is added <strong>unescaped</strong> to user
                  lookups, so be VERY careful with the query you specify.
                </div>
                <span class="config-string-field" binding="config.LDAP_USER_FILTER" is-optional="true"
                      placeholder="someOtherField=someOtherValue"
                      pattern="\((.+)\)"></span>
                <div class="help-text">
                  If specified, the additional filter used for all user lookup queries. Note that all
                  Distinguished Names used in the filter must be <strong>full</strong> paths; the <code>base_dn</code>
                  is <strong>not</strong> added automatically here. <strong>Must</strong> be wrapped in parens.
                </div>
                <div class="help-text">
                  Example: (someOtherField=someOtherValue)
                </div>
                <div class="help-text">
                  Example: (memberOf=some.full.path.to.a.group)
                </div>
                <div class="help-text">
                  Example: (|(someFirstField=someValue)(someOtherField=someOtherValue))
                </div>
                <div class="help-text">
                  Example: (&(someFirstField=someValue)(someOtherField=someOtherValue))
                </div>
              </td>
            </tr>
            <tr>
              <td>Administrator DN:</td>
              <td><span class="config-string-field" binding="config.LDAP_ADMIN_DN"></span>
                <div class="help-text">
                  The Distinguished Name for the Administrator account. This account must be able to login and view
                  the records for all user accounts.
                </div>
                <div class="help-text">
                  Example: uid=admin,ou=employees,dc=my,dc=domain,dc=com
                </div>
              </td>
            </tr>
            <tr>
              <td>Administrator DN Password:</td>
              <td>
                <div class="co-alert co-alert-warning" style="margin-bottom: 10px;">
                  Note: This will be stored in
                  <strong>plaintext</strong> inside the config.yaml, so setting up a dedicated account or using
                  <a href="http://tools.ietf.org/id/draft-stroeder-hashed-userpassword-values-01.html" ng-safenewtab>a
                    password hash</a> is <strong>highly</strong> recommended.
                </div>
                <input class="form-control" type="password" ng-model="config.LDAP_ADMIN_PASSWD">
                <div class="help-text">
                  The password for the Administrator DN.
                </div>
              </td>
            </tr>
            <tr>
              <td>UID Attribute:</td>
              <td>
                <span class="config-string-field" binding="config.LDAP_UID_ATTR" default-value="uid"></span>
                <div class="help-text">
                  The name of the property field in your LDAP user records that stores your
                  users' username. Typically "uid".
                </div>
              </td>
            </tr>
            <tr>
              <td>Mail Attribute:</td>
              <td>
                <span class="config-string-field" binding="config.LDAP_EMAIL_ATTR" default-value="mail"></span>
                <div class="help-text">
                  The name of the property field in your LDAP user records that stores your
                  users' e-mail address(es). Typically "mail".
                </div>
              </td>
            </tr>
            <tr ng-if="config.LDAP_URI.indexOf('ldaps://') == 0">
              <td class="non-input">Custom TLS Certificate:</td>
              <td>
                <span class="config-file-field" filename="ldap.crt" has-file="hasfile.LDAPTLSCert"></span>
                <div class="help-text">
                  If specified, the certificate (in PEM format) for the LDAP TLS connection.
                </div>
              </td>
            </tr>
            <tr ng-if="config.LDAP_URI.indexOf('ldaps://') == 0">
              <td class="non-input">Allow insecure:</td>
              <td>
                <div class="config-bool-field" binding="config.LDAP_ALLOW_INSECURE_FALLBACK">
                  Allow fallback to non-TLS connections
                </div>
                <div class="help-text">
                  If enabled, LDAP will fallback to <strong>insecure non-TLS connections</strong> if TLS does not
                  succeed.
                </div>
              </td>
            </tr>
          </table>
        </div>
      </div> <!-- / Internal Authentication -->

      <!-- External Authentication -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa fa-id-card"></i> External Authorization (OAuth)
        </div>
        <div class="co-panel-body">
          <!-- GitHub Authentication -->
          <div class="co-panel">
            <div class="co-panel-heading">
              <i class="fab fa-github"></i> GitHub (Enterprise) Authentication
            </div>
            <div class="co-panel-body">
              <div class="description">
                <p>
                  If enabled, users can use GitHub or GitHub Enterprise to authenticate to the registry.
                </p>
                <p>
                  <strong>Note:</strong> A registered GitHub (Enterprise) OAuth application is required.
                  View instructions on how to
                  <a href="https://access.redhat.com/documentation/en-us/red_hat_quay/3/html-single/use_red_hat_quay/index#github-app" ng-safenewtab>
                    Create an OAuth Application in GitHub
                  </a>
                </p>
              </div>

              <div class="config-bool-field" binding="config.FEATURE_GITHUB_LOGIN">
                Enable GitHub Authentication
              </div>

              <div class="co-alert co-alert-warning"
                   ng-if="config.FEATURE_GITHUB_LOGIN && config.AUTHENTICATION_TYPE && config.AUTHENTICATION_TYPE != 'Database' && config.AUTHENTICATION_TYPE != 'AppToken' && !config.GITHUB_LOGIN_CONFIG.LOGIN_BINDING_FIELD">
                Warning: This provider is not bound to your <strong>{{ config.AUTHENTICATION_TYPE }}</strong>
                authentication. Logging in via this provider will create a <strong><span
                                                                                     class="registry-name"></span>-only user</strong>, which is not the recommended approach. It is
                <strong>highly</strong> recommended to choose a "Binding Field" below.
              </div>

              <table class="config-table" ng-if="config.FEATURE_GITHUB_LOGIN">
                <tr>
                  <td>GitHub:</td>
                  <td>
                    <select class="form-control" ng-model="mapped.GITHUB_LOGIN_KIND">
                      <option value="hosted">GitHub.com</option>
                      <option value="enterprise">GitHub Enterprise</option>
                    </select>
                  </td>
                </tr>
                <tr ng-if="mapped.GITHUB_LOGIN_KIND == 'enterprise'">
                  <td>GitHub Endpoint:</td>
                  <td>
                    <span class="config-string-field" binding="config.GITHUB_LOGIN_CONFIG.GITHUB_ENDPOINT"
                          placeholder="https://my.githubserver" pattern="{{ GITHOST_REGEX }}">
                    </span>
                    <div class="help-text">
                      The GitHub Enterprise endpoint. Must start with http:// or https://.
                    </div>
                  </td>
                </tr>
                <tr>
                  <td>OAuth Client ID:</td>
                  <td>
                    <span class="config-string-field" binding="config.GITHUB_LOGIN_CONFIG.CLIENT_ID">
                    </span>
                  </td>
                </tr>
                <tr>
                  <td>OAuth Client Secret:</td>
                  <td>
                    <span class="config-string-field" binding="config.GITHUB_LOGIN_CONFIG.CLIENT_SECRET">
                    </span>
                  </td>
                </tr>
                <tr>
                  <td>Organization Filtering:</td>
                  <td>
                    <div class="config-bool-field" binding="config.GITHUB_LOGIN_CONFIG.ORG_RESTRICT">
                      Restrict By Organization Membership
                    </div>

                    <div class="help-text" style="margin-bottom: 20px;">
                      If enabled, only members of specified GitHub
                      <span ng-if="mapped.GITHUB_LOGIN_KIND == 'enterprise'">Enterprise</span> organizations will be
                      allowed to login via GitHub
                      <span ng-if="mapped.GITHUB_LOGIN_KIND == 'enterprise'">Enterprise</span>.
                    </div>

                    <span class="config-list-field" item-title="Organization ID"
                          binding="config.GITHUB_LOGIN_CONFIG.ALLOWED_ORGANIZATIONS"
                          ng-if="config.GITHUB_LOGIN_CONFIG.ORG_RESTRICT">
                    </span>
                  </td>
                </tr>
                <tr ng-if="config.AUTHENTICATION_TYPE != 'Database' && config.AUTHENTICATION_TYPE != 'AppToken'">
                  <td>Binding Field:</td>
                  <td>
                    <select class="form-control" ng-model="config.GITHUB_LOGIN_CONFIG.LOGIN_BINDING_FIELD">
                      <option value="">(None)</option>
                      <option value="sub">Subject (User ID)</option>
                      <option value="username">Username</option>
                      <option value="email">E-mail address</option>
                    </select>
                    <div class="help-text">
                      If selected, when a user logs in via this provider, they will be automatically bound to their
                      user in <strong>{{ config.AUTHENTICATION_TYPE }}</strong> by matching the selected field from
                      the provider to the associated user in {{ config.AUTHENTICATION_TYPE }}.
                    </div>
                    <div class="help-text">
                      For example, selecting <code>Subject</code> here with a backing authentication system of LDAP
                      means that a user logging in via this provider will also be bound to their user in LDAP by
                      username.
                    </div>
                    <div class="help-text">
                      If none selected, a <strong>user unique to <span class="registry-name"></span></strong> will be
                      created on initial login with this provider. <strong>This is not the recommended setup.</strong>
                    </div>
                  </td>
                </tr>
              </table>
            </div>
          </div> <!-- /GitHub Authentication -->

          <!-- Google Authentication -->
          <div class="co-panel">
            <div class="co-panel-heading">
              <i class="fab fa-google"></i> Google Authentication
            </div>
            <div class="co-panel-body">
              <div class="description">
                <p>
                  If enabled, users can use Google to authenticate to the registry.
                </p>
                <p>
                  <strong>Note:</strong> A registered Google OAuth application is required.
                  Visit the
                  <a href="https://console.developers.google.com" ng-safenewtab>
                    Google Developer Console
                  </a>
                  to register an application.
                </p>
              </div>

              <div class="config-bool-field" binding="config.FEATURE_GOOGLE_LOGIN">
                Enable Google Authentication
              </div>

              <div class="co-alert co-alert-warning"
                   ng-if="config.FEATURE_GOOGLE_LOGIN && config.AUTHENTICATION_TYPE && config.AUTHENTICATION_TYPE != 'Database' && config.AUTHENTICATION_TYPE != 'AppToken' && !config.GOOGLE_LOGIN_CONFIG.LOGIN_BINDING_FIELD">
                Warning: This provider is not bound to your <strong>{{ config.AUTHENTICATION_TYPE }}</strong>
                authentication. Logging in via this provider will create a <strong><span
                                                                                     class="registry-name"></span>-only user</strong>, which is not the recommended approach. It is
                <strong>highly</strong> recommended to choose a "Binding Field" below.
              </div>

              <table class="config-table" ng-if="config.FEATURE_GOOGLE_LOGIN">
                <tr>
                  <td>OAuth Client ID:</td>
                  <td>
                    <span class="config-string-field" binding="config.GOOGLE_LOGIN_CONFIG.CLIENT_ID">
                    </span>
                  </td>
                </tr>
                <tr>
                  <td>OAuth Client Secret:</td>
                  <td>
                    <span class="config-string-field" binding="config.GOOGLE_LOGIN_CONFIG.CLIENT_SECRET">
                    </span>
                  </td>
                </tr>
                <tr ng-if="config.AUTHENTICATION_TYPE != 'Database' && config.AUTHENTICATION_TYPE != 'AppToken'">
                  <td>Binding Field:</td>
                  <td>
                    <select class="form-control" ng-model="config.GOOGLE_LOGIN_CONFIG.LOGIN_BINDING_FIELD">
                      <option value="">(None)</option>
                      <option value="sub">Subject (User ID)</option>
                      <option value="username">Username</option>
                      <option value="email">E-mail address</option>
                    </select>
                    <div class="help-text">
                      If selected, when a user logs in via this provider, they will be automatically bound to their
                      user in <strong>{{ config.AUTHENTICATION_TYPE }}</strong> by matching the selected field from
                      the provider to the associated user in {{ config.AUTHENTICATION_TYPE }}.
                    </div>
                    <div class="help-text">
                      For example, selecting <code>Subject</code> here with a backing authentication system of LDAP
                      means that a user logging in via this provider will also be bound to their user in LDAP by
                      username.
                    </div>
                    <div class="help-text">
                      If none selected, a <strong>user unique to <span class="registry-name"></span></strong> will be
                      created on initial login with this provider. <strong>This is not the recommended setup.</strong>
                    </div>
                  </td>
                </tr>
              </table>
            </div>
          </div> <!-- /Google Authentication -->

          <!-- Custom OIDC providers -->
          <div class="co-panel" ng-repeat="provider in getOIDCProviders(config)">
            <div class="co-panel-heading">
              <span class="icon-image-view" value="{{ config[provider]['SERVICE_ICON'] || 'fa-user-circle' }}"
                    style="margin-right: 6px;"></span>
              {{ config[provider]['SERVICE_NAME'] || (getOIDCProviderId(provider) + ' Authentication') }}
              <span style="display: inline-block; margin-left: 10px">(<a href="javascript:void(0)"
                                                                         ng-click="removeOIDCProvider(provider)">Delete</a>)</span>
            </div>
            <div class="co-panel-body">
              <div class="co-alert co-alert-warning"
                   ng-if="config.AUTHENTICATION_TYPE && config.AUTHENTICATION_TYPE != 'Database' && config.AUTHENTICATION_TYPE != 'AppToken' && !(config[provider].LOGIN_BINDING_FIELD)">
                Warning: This OIDC provider is not bound to your <strong>{{ config.AUTHENTICATION_TYPE }}</strong>
                authentication. Logging in via this provider will create a <strong><span
                                                                                     class="registry-name"></span>-only user</strong>, which is not the recommended approach. It is
                <strong>highly</strong> recommended to choose a "Binding Field" below.
              </div>

              <table class="config-table">
                <tr>
                  <td class="non-input">Service ID:</td>
                  <td>
                    <code>{{ getOIDCProviderId(provider) }}</code>
                  </td>
                </tr>
                <tr>
                  <td>OIDC Server:</td>
                  <td>
                    <span class="config-string-field" binding="config[provider].OIDC_SERVER"
                          placeholder="https://path/to/oidc/compliant/server" pattern="https://.+">
                    </span>
                    <div class="help-text">
                      The URL of an OIDC-compliant server.
                    </div>
                  </td>
                </tr>
                <tr>
                  <td>Client ID:</td>
                  <td>
                    <span class="config-string-field" binding="config[provider].CLIENT_ID"></span>
                  </td>
                </tr>
                <tr>
                  <td>Client Secret:</td>
                  <td>
                    <span class="config-string-field" binding="config[provider].CLIENT_SECRET"></span>
                  </td>
                </tr>
                <tr>
                  <td>Service Name:</td>
                  <td>
                    <span class="config-string-field" binding="config[provider].SERVICE_NAME"
                          placeholder="My Authentication Service">
                    </span>
                    <div class="help-text">
                      The user friendly name to display for the service on the login page.
                    </div>
                  </td>
                </tr>
                <tr>
                  <td>Service Icon (optional):</td>
                  <td>
                    <span class="config-string-field" binding="config[provider].SERVICE_ICON"
                          placeholder="URL of the icon to use for this service OR a font awesome CSS name"
                          is-optional="true">
                    </span>
                    <div class="help-text">
                      If specified, the icon to display for this login service on the login page. Can be either a URL
                      to an icon or a CSS class name from <a href="http://fontawesome.io" ng-safenewtab>Font
                      Awesome</a>
                    </div>
                  </td>
                </tr>
                <tr>
                  <td>Verified E-mail Address Claim (optional):</td>
                  <td>
                    <span class="config-string-field" binding="config[provider].VERIFIED_EMAIL_CLAIM_NAME"
                          placeholder="User information JWT claim that contains the verified e-mail" is-optional="true">
                    </span>
                    <div class="help-text">
                      If specified, the claim in the User Information JWT that contains the verified e-mail address
                      for the user.
                    </div>
                  </td>
                </tr>
                <tr>
                  <td>Preferred Username Claim (optional):</td>
                  <td>
                    <span class="config-string-field" binding="config[provider].PREFERRED_USERNAME_CLAIM_NAME"
                          placeholder="User information JWT claim that contains the preferred username" is-optional="true">
                    </span>
                    <div class="help-text">
                      If specified, the claim in the User Information JWT that contains the preferred username for the
                      user.
                    </div>
                  </td>
                </tr>
                <tr ng-if="config.AUTHENTICATION_TYPE != 'Database' && config.AUTHENTICATION_TYPE != 'AppToken'">
                  <td>Binding Field:</td>
                  <td>
                    <select class="form-control" ng-model="config[provider].LOGIN_BINDING_FIELD">
                      <option value="">(None)</option>
                      <option value="sub">Subject (User ID)</option>
                      <option value="username">Username</option>
                      <option value="email">E-mail address</option>
                    </select>
                    <div class="help-text">
                      If selected, when a user logs in via this OIDC provider, they will be automatically bound to
                      their user in <strong>{{ config.AUTHENTICATION_TYPE }}</strong> by matching the selected field
                      from the OIDC provider to the associated user in {{ config.AUTHENTICATION_TYPE }}.
                    </div>
                    <div class="help-text">
                      For example, selecting <code>Subject</code> here with a backing authentication system of LDAP
                      means that a user logging in via this OIDC provider will also be bound to their user in LDAP by
                      username.
                    </div>
                    <div class="help-text">
                      If none selected, a <strong>user unique to <span class="registry-name"></span></strong> will be
                      created on initial login with this OIDC provider. <strong>This is not the recommended
                      setup.</strong>
                    </div>
                  </td>
                </tr>
                <tr>
                  <td>Login Scopes:</td>
                  <td>
                    <span class="config-list-field" item-title="Login Scope"
                          binding="config[provider].LOGIN_SCOPES"></span>
                    <div class="help-text">
                      If specified, the scopes to send to the OIDC provider when performing the login flow. Note that,
                      <strong>if specified</strong>, these scopes will
                      <strong>override</strong> those set by default, so this list <strong>must</strong> include a
                      scope for OpenID Connect
                      (typically the <code>openid</code> scope) or this provider will fail.
                    </div>
                  </td>
                </tr>
              </table>
              <div>
                <h4>Callback URLs for this service:</h4>
                <ul>
                  <li>
                    <code>{{ mapped.TLS_SETTING == 'none' ? 'http' : 'https' }}://{{ config.SERVER_HOSTNAME || '(configure server hostname)' }}/oauth2/{{ getOIDCProviderId(provider).toLowerCase() }}/callback</code>
                  </li>
                  <li>
                    <code>{{ mapped.TLS_SETTING == 'none' ? 'http' : 'https' }}://{{ config.SERVER_HOSTNAME || '(configure server hostname)' }}/oauth2/{{ getOIDCProviderId(provider).toLowerCase() }}/callback/attach</code>
                  </li>
                  <li>
                    <code>{{ mapped.TLS_SETTING == 'none' ? 'http' : 'https' }}://{{ config.SERVER_HOSTNAME || '(configure server hostname)' }}/oauth2/{{ getOIDCProviderId(provider).toLowerCase() }}/callback/cli</code>
                  </li>
                </ul>
              </div>
            </div>
          </div>

          <!-- Add Provider -->
          <a class="btn btn-default" ng-click="addOIDCProvider()" style="margin-right: 6px;">Add OIDC Provider</a>
          <a href="http://openid.net/connect/" ng-safenewtab>What is OIDC?</a>
        </div>
      </div> <!-- /External Authentication -->

      <!-- Access settings -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa fa-user-circle"></i> Access Settings
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>Various settings around access and authentication to the registry.</p>
          </div>

          <table class="config-table">
            <tr>
              <td class="non-input">Basic Credentials Login:</td>
              <td colspan="2">
                <div class="config-bool-field" binding="config.FEATURE_DIRECT_LOGIN"
                     ng-if="getOIDCProviders(config).length || config.FEATURE_GITHUB_LOGIN || config.FEATURE_GOOGLE_LOGIN">
                  Login to User Interface via credentials
                </div>
                <div
                  ng-if="!getOIDCProviders(config).length && !config.FEATURE_GITHUB_LOGIN && !config.FEATURE_GOOGLE_LOGIN">
                  <div ng-if="!config.FEATURE_DIRECT_LOGIN" class="co-alert co-alert-danger">
                    Login to User Interface via credentials must be enabled. <a
                                                                               ng-click="enableFeature(config, 'FEATURE_DIRECT_LOGIN')">Click here to enable</a>.
                  </div>
                  <div ng-if="config.FEATURE_DIRECT_LOGIN">
                    Login to User Interface via credentials is <strong>enabled</strong> (requires at least one OIDC
                    provider to disable)
                  </div>
                </div>
                <div class="help-text">
                  If enabled, users will be able to login to the <strong>user interface</strong> via their username
                  and password credentials.
                </div>
                <div class="help-text">
                  If <strong>disabled</strong>, users will only be able to login to the <strong>user
                  interface</strong> via one of the configured External Authentication providers.
                </div>
              </td>
            </tr>
            <tr>
              <td class="non-input">External Application tokens</td>
              <td colspan="2">
                <div class="config-bool-field" binding="config.FEATURE_APP_SPECIFIC_TOKENS">
                  Allow external application tokens
                </div>
                <div class="help-text">
                  If enabled, users will be able to generate external application tokens for use on the Docker and rkt
                  CLI. Note
                  that these tokens will <strong>not be required</strong> unless "App Token" is chosen as the Internal
                  Authentication method above.
                </div>
              </td>
            </tr>
            <tr ng-if="config.FEATURE_APP_SPECIFIC_TOKENS">
              <td>External application token expiration</td>
              <td colspan="2">
                <span class="config-string-field" binding="config.APP_SPECIFIC_TOKEN_EXPIRATION"
                      pattern="[0-9]+(m|w|h|d|s)" is-optional="true"></span>
                <div class="help-text">
                  The expiration time for user generated external application tokens. If none, tokens will never
                  expire.
                </div>
              </td>
            </tr>
            <tr>
              <td class="non-input">Anonymous Access:</td>
              <td colspan="2">
                <div class="config-bool-field" binding="config.FEATURE_ANONYMOUS_ACCESS">
                  Enable Anonymous Access
                </div>
                <div class="help-text">
                  If enabled, public repositories and search can be accessed by anyone that can
                  reach the registry, even if they are not authenticated. Disable to only allow
                  authenticated users to view and pull "public" resources.
                </div>
              </td>
            </tr>
            <tr>
              <td class="non-input">User Creation:</td>
              <td colspan="2">
                <div class="config-bool-field" binding="config.FEATURE_USER_CREATION">
                  Enable Non-Superuser User Creation
                </div>
                <div class="help-text">
                  If enabled, user accounts can be created by anyone (unless restricted below to invited users).
                  Users can always be created in the users panel in this superuser tool, even if this feature is
                  disabled. If disabled, users can <strong>ONLY</strong> be created in the superuser tool or
                  via team sync.
                </div>
              </td>
            </tr>
            <tr ng-show="config.FEATURE_USER_CREATION && config.FEATURE_MAILING">
              <td class="non-input">Invite-only User Creation:</td>
              <td colspan="2">
                <div class="config-bool-field" binding="config.FEATURE_INVITE_ONLY_USER_CREATION">
                  Enable Invite-only User Creation
                </div>
                <div class="help-text">
                  If enabled, user accounts can only be created when a user has been invited, by e-mail address, to
                  join a team.
                  Users can always be created in the users panel in this superuser tool, even if this feature is
                  enabled.
                </div>
              </td>
            </tr>
            <tr>
              <td class="non-input">Encrypted Client Password:</td>
              <td colspan="2">
                <div class="config-bool-field" binding="config.FEATURE_REQUIRE_ENCRYPTED_BASIC_AUTH">
                  Require Encrypted Client Passwords
                </div>
                <div class="help-text">
                  If enabled, users will not be able to login from the Docker command
                  line with a non-encrypted password and must generate an encrypted
                  password to use.
                </div>
                <div class="help-text" ng-if="config.AUTHENTICATION_TYPE != 'Database'">
                  This feature is <strong>highly recommended</strong> for setups with external authentication, as
                  Docker currently stores passwords in <strong>plaintext</strong> on user's machines.
                </div>
              </td>
            </tr>
            <tr>
              <td class="non-input">Prefix username autocompletion:</td>
              <td colspan="2">
                <div class="config-bool-field" binding="config.FEATURE_PARTIAL_USER_AUTOCOMPLETE">
                  Allow prefix username autocompletion
                </div>
                <div class="help-text">
                  If disabled, autocompletion for users will only match on exact usernames.
                </div>
              </td>
            </tr>
            <tr ng-show="config.FEATURE_MAILING">
              <td class="non-input">Team Invitations:</td>
              <td colspan="2">
                <div class="config-bool-field" binding="config.FEATURE_REQUIRE_TEAM_INVITE">
                  Require Team Invitations
                </div>
                <div class="help-text">
                  If enabled, when adding a new user to a team, they will receive an invitation to join the team, with
                  the option to decline.
                  Otherwise, users will be immediately part of a team when added by a team administrator.
                </div>
              </td>
            </tr>
          </table>
        </div>
      </div> <!-- /Access settings -->

      <!-- Registry settings -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fas fa-cog"></i> Registry Protocol Settings
        </div>
        <div class="co-panel-body">
          <div class="co-alert co-alert-warning" style="margin-bottom: 20px">
            Docker V1 protocol support has been <strong>officially deprecated</strong> by Quay and support will be
            removed in an upcoming version. It is <strong>strongly</strong> suggested to have this
            flag enabled and to restrict access to V1 push.
          </div>
          <div class="config-bool-field" binding="config.FEATURE_RESTRICTED_V1_PUSH">
            Restrict V1 Push Support
          </div>
          <div class="help-text">
            If enabled, Docker V1 push protocol will only be supported by those namespaces whitelisted
            below. This feature should be left on unless <strong>general usage</strong> of the older
            Docker V1 protocol is necessary.
          </div>
          <div ng-if="config.FEATURE_RESTRICTED_V1_PUSH" style="margin-top: 20px;">
            <strong>Namespace whitelist:</strong>
            <span class="config-list-field" item-title="Namespace" binding="config.V1_PUSH_WHITELIST"
                  item-pattern="[a-z0-9-]"></span>
            <div class="help-text">
              The list of namespaces in which V1 push is still enabled.
            </div>
          </div>
        </div>
      </div>

      <!-- Build Support -->
      <div class="co-panel">
        <div class="co-panel-heading">
          <i class="fa fa-tasks"></i> Dockerfile Build Support
        </div>
        <div class="co-panel-body">
          <div class="description">
            If enabled, users can submit Dockerfile's to be built and pushed by <span class="registry-name"></span>.
          </div>

          <div class="config-bool-field" binding="config.FEATURE_BUILD_SUPPORT">
            Enable Dockerfile Build
          </div>

          <div ng-if="config.FEATURE_BUILD_SUPPORT" style="margin-top: 10px">
            <strong>Note: Build workers are required for this feature.</strong>
            See <a href="https://access.redhat.com/documentation/en-us/red_hat_quay/3/html-single/use_red_hat_quay/index#set-up-the-build-workers" ng-safenewtab>Adding Build
              Workers</a> for instructions on how to setup build workers.
          </div>
        </div>
      </div> <!-- /Build Support -->

      <!-- GitHub Trigger -->
      <div class="co-panel" ng-if="config.FEATURE_BUILD_SUPPORT" style="margin-top: 20px;">
        <div class="co-panel-heading">
          <i class="fab fa-github"></i> GitHub (Enterprise) Build Triggers
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>
              If enabled, users can setup GitHub or GitHub Enterprise triggers to invoke Registry builds.
            </p>
            <p>
              <strong>Note:</strong> A registered GitHub (Enterprise) OAuth application (<strong>separate from GitHub
                Authentication</strong>) is required.
              View instructions on how to
              <a href="https://access.redhat.com/documentation/en-us/red_hat_quay/3/html-single/use_red_hat_quay/index#github-app" ng-safenewtab>
                Create an OAuth Application in GitHub
              </a>
            </p>
          </div>

          <div class="config-bool-field" binding="config.FEATURE_GITHUB_BUILD">
            Enable GitHub Triggers
          </div>

          <table class="config-table" ng-if="config.FEATURE_GITHUB_BUILD">
            <tr>
              <td>GitHub:</td>
              <td>
                <select class="form-control" ng-model="mapped.GITHUB_TRIGGER_KIND">
                  <option value="hosted">GitHub.com</option>
                  <option value="enterprise">GitHub Enterprise</option>
                </select>
              </td>
            </tr>
            <tr ng-if="mapped.GITHUB_TRIGGER_KIND == 'enterprise'">
              <td>GitHub Endpoint:</td>
              <td>
                <span class="config-string-field" binding="config.GITHUB_TRIGGER_CONFIG.GITHUB_ENDPOINT"
                      placeholder="https://my.githubserver" pattern="{{ GITHOST_REGEX }}">
                </span>
                <div class="help-text">
                  The GitHub Enterprise endpoint. Must start with http:// or https://.
                </div>
              </td>
            </tr>
            <tr>
              <td>OAuth Client ID:</td>
              <td>
                <span class="config-string-field" binding="config.GITHUB_TRIGGER_CONFIG.CLIENT_ID">
                </span>
              </td>
            </tr>
            <tr>
              <td>OAuth Client Secret:</td>
              <td>
                <span class="config-string-field" binding="config.GITHUB_TRIGGER_CONFIG.CLIENT_SECRET">
                </span>
              </td>
            </tr>
          </table>
        </div>
      </div> <!-- /GitHub Trigger -->

      <!-- BitBucket Trigger -->
      <div class="co-panel" ng-if="config.FEATURE_BUILD_SUPPORT" style="margin-top: 20px;">
        <div class="co-panel-heading">
          <i class="fab fa-bitbucket"></i> BitBucket Build Triggers
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>
              If enabled, users can setup BitBucket triggers to invoke Registry builds.
            </p>
            <p>
              <strong>Note:</strong> A registered BitBucket OAuth application is required.
            </p>
          </div>

          <div class="config-bool-field" binding="config.FEATURE_BITBUCKET_BUILD">
            Enable BitBucket Triggers
          </div>

          <table class="config-table" ng-if="config.FEATURE_BITBUCKET_BUILD">
            <tr>
              <td>OAuth Consumer Key:</td>
              <td>
                <span class="config-string-field" binding="config.BITBUCKET_TRIGGER_CONFIG.CONSUMER_KEY">
                </span>
              </td>
            </tr>
            <tr>
              <td>OAuth Consumer Secret:</td>
              <td>
                <span class="config-string-field" binding="config.BITBUCKET_TRIGGER_CONFIG.CONSUMER_SECRET">
                </span>
              </td>
            </tr>
          </table>
        </div>
      </div> <!-- /BitBucket Trigger -->

      <!-- GitLab Trigger -->
      <div class="co-panel" ng-if="config.FEATURE_BUILD_SUPPORT" style="margin-top: 20px;">
        <div class="co-panel-heading">
          <i class="fab fa-gitlab"></i> GitLab Build Triggers
        </div>
        <div class="co-panel-body">
          <div class="description">
            <p>
              If enabled, users can setup GitLab triggers to invoke Registry builds.
            </p>
            <p>
              <strong>Note:</strong> A registered GitLab OAuth application is required.
              Visit the
              <a href="{{ config.GITLAB_TRIGGER_CONFIG.GITLAB_ENDPOINT || 'https://gitlab.com' }}/profile/applications"
                 ng-safenewtab>
                GitLab applications admin panel
              </a>
              to create a new application.
            </p>
            <p>The callback URL to use is:&nbsp;&nbsp;
              <code>{{ config.PREFERRED_URL_SCHEME || 'http' }}://{{ config.SERVER_HOSTNAME || 'localhost' }}/oauth2/gitlab/callback/trigger</code>
            </p>
          </div>

          <div class="config-bool-field" binding="config.FEATURE_GITLAB_BUILD">
            Enable GitLab Triggers
          </div>

          <table class="config-table" ng-if="config.FEATURE_GITLAB_BUILD">
            <tr>
              <td>GitLab:</td>
              <td>
                <select class="form-control" ng-model="mapped.GITLAB_TRIGGER_KIND">
                  <option value="hosted">GitLab.com</option>
                  <option value="enterprise">GitLab CE/EE</option>
                </select>
              </td>
            </tr>
            <tr ng-if="mapped.GITLAB_TRIGGER_KIND == 'enterprise'">
              <td>GitLab Endpoint:</td>
              <td>
                <span class="config-string-field" binding="config.GITLAB_TRIGGER_CONFIG.GITLAB_ENDPOINT"
                      placeholder="https://my.gitlabserver" pattern="{{ GITHOST_REGEX }}">
                </span>
                <div class="help-text">
                  The GitLab Enterprise endpoint. Must start with http:// or https://.
                </div>
              </td>
            </tr>
            <tr>
              <td>Application Id:</td>
              <td>
                <span class="config-string-field" binding="config.GITLAB_TRIGGER_CONFIG.CLIENT_ID">
                </span>
              </td>
            </tr>
            <tr>
              <td>Secret:</td>
              <td>
                <span class="config-string-field" binding="config.GITLAB_TRIGGER_CONFIG.CLIENT_SECRET">
                </span>
              </td>
            </tr>
          </table>
        </div>
      </div> <!-- /GitLab Trigger -->
    </form>

    <!-- Save Bar -->
    <div class="cor-floating-bottom-bar">
      <button class="btn" ng-class="mapped.$hasChanges ? 'btn-primary' : 'btn-success'"
              ng-click="checkValidateAndSave()" ng-show="configform.$valid">
        <i class="fa fa-lg" ng-class="mapped.$hasChanges ? 'fa-dot-circle-o' : 'fa-check-circle'"></i>
        <span ng-if="mapped.$hasChanges">Save Configuration Changes</span>
        <span ng-if="!mapped.$hasChanges">Configuration Saved</span>
      </button>
      <button class="btn btn-warning" ng-click="checkValidateAndSave()" ng-show="!configform.$valid"
              ng-click="checkValidateAndSave()">
        <i class="fa fa-lg fa-sort"></i>
        <span ng-if="configform.$error['required'].length">
          {{ configform.$error['required'].length }} configuration field<span
                                                                          ng-show="configform.$error['required'].length != 1">s</span> remaining
        </span>
        <span ng-if="!configform.$error['required'].length">
          Invalid configuration field
        </span>
      </button>
    </div>

    <!-- Modal message dialog -->
    <div class="modal co-dialog fade initial-setup-modal" id="validateAndSaveModal">
      <div class="modal-dialog">
        <div class="modal-content">
          <div class="modal-header">
            <span class="cor-step-bar">
              <span class="cor-step active" title="Configure Database" text="1"></span>
              <span class="cor-step active" title="Setup Database" icon="database"></span>
              <span class="cor-step active" title="Create Superuser" text="2"></span>
              <span class="cor-step active" title="Configure Registry" text="3"></span>
              <span class="cor-step active" title="Validate Configuration" text="4"></span>
              <span class="cor-step" title="Setup Complete" icon="download"></span>
            </span>
            <h4 class="modal-title">
              Checking your settings
            </h4>
          </div>
          <div class="modal-body">
            <div class="service-verification">
              <div class="service-verification-row" ng-repeat="serviceInfo in validating">
                <span class="quay-spinner" ng-show="serviceInfo.status == 'validating'"></span>
                <i class="fa fa-lg fa-check-circle" ng-show="serviceInfo.status == 'success'"></i>
                <i class="fa fa-lg fa-warning" ng-show="serviceInfo.status == 'error'"></i>
                <span class="service-title">{{ serviceInfo.service.title }}</span>

                <div class="service-verification-error" ng-show="serviceInfo.status == 'error'">
                  {{ serviceInfo.errorMessage }}</div>
              </div>
            </div>
          </div>

          <!-- Footer: Saving configuration -->
          <div class="modal-footer working" ng-show="savingConfiguration">
            <span class="cor-loader-inline"></span> Saving Configuration...
          </div>

          <!-- Footer: Validating -->
          <div class="modal-footer working"
               ng-show="!savingConfiguration && validationStatus(validating) == 'validating'">
            <span class="cor-loader-inline"></span> Validating settings...

            <button class="btn btn-default" ng-click="cancelValidation()">
              Stop Validating
            </button>
          </div>

          <!-- Footer: Valid Config -->
          <div class="modal-footer" ng-show="!savingConfiguration && validationStatus(validating) == 'success'">
            <span class="left-align">
              <i class="fa fa-check"></i>
              Configuration Validated
            </span>

            <button class="btn" data-dismiss="modal">
              Continue Editing
            </button>
            <button class="btn btn-primary" ng-click="saveConfiguration()" ng-disabled="savingConfiguration">
              Next
            </button>
          </div>

          <!-- Footer: Invalid Config -->
          <div class="modal-footer" ng-show="!savingConfiguration && validationStatus(validating) == 'failed'">
            <span class="left-align">
              <i class="fa fa-warning"></i>
              Problem Detected
            </span>

            <button class="btn btn-default" data-dismiss="modal">
              Continue Editing
            </button>
          </div>

        </div><!-- /.modal-content -->
      </div><!-- /.modal-dialog -->
    </div><!-- /.modal -->

  </div>
